#include "dialog.h"
#include "ui_dialog.h"

#include <QFileDialog>
#include <QMessageBox>
#include <QDebug>

/**
 * @brief c_NotFound
 *
 * This value will be inserted in every field in the output csv file where
 * we need to indicate that a value was not found
 */
static const QString c_NotFound("<NOT FOUND>");

static const QString c_DynamicGSOID("Dynamic GSOID");
static const QString c_InitialValue("Initial Value");
static const QString c_Access("Access in XML");
static const QString c_AccessMatches("Access Matches");
static const QString c_Poll("Poll");

static const QChar c_Comma(',');
static const QString c_True("true");
static const QString c_False("false");

static const QRegExp c_EverythingExceptForLetters("[^a-zA-Z]");

static const AttributeProperties c_DefaultAttributeProperties(c_NotFound, c_NotFound, c_False);

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog),
    m_StoreFirstMatchOnly(false),
    m_XmlParser(),
    m_CsvParser()
{
    ui->setupUi(this);
    SetupConnections_();
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::OnInputButtonClicked_()
{
    m_InputXmlFilePath = QFileDialog::getOpenFileName(
        0,
        "Please select xml config file",
        QString(),
        "Xml files (*.xml)");

    if (!m_InputXmlFilePath.isEmpty())
    {
        ui->InputLabel->setText(m_InputXmlFilePath);
        UpdateGenerateButton_();
        ReadXmlFile_();
    }
}

void Dialog::OnInputButton2Clicked_()
{
    m_InputCsvFilePath = QFileDialog::getOpenFileName(
        0,
        "Please select csv file containing attributes we are looking for",
        QString(),
        "Csv files (*.csv)");

    if (!m_InputCsvFilePath.isEmpty())
    {
        ui->InputLabel2->setText(m_InputCsvFilePath);
        UpdateGenerateButton_();
        ReadCsvFile_();
    }
}

void Dialog::OnGenerateButtonClicked_()
{
    FindAttributesInPanelData_();
}

void Dialog::OnCheckBoxStateChanged_(int State)
{
    switch (State)
    {
    case Qt::Unchecked:
        m_StoreFirstMatchOnly = false;
        break;

    case Qt::Checked:
        m_StoreFirstMatchOnly = true;
        break;

    // We don't allow for other states
    default:
        Q_ASSERT("Invalid CheckBox State!" == 0);
        break;
    }
}

void Dialog::SetupConnections_()
{
    bool ConnectOK = false;
    ConnectOK = connect
    (
        ui->InputButton,
        SIGNAL(clicked()),
        this,
        SLOT(OnInputButtonClicked_())
    );
    Q_ASSERT(ConnectOK);

    ConnectOK = connect
    (
        ui->InputButton2,
        SIGNAL(clicked()),
        this,
        SLOT(OnInputButton2Clicked_())
    );
    Q_ASSERT(ConnectOK);

    ConnectOK = connect
    (
        ui->GenerateButton,
        SIGNAL(clicked()),
        this,
        SLOT(OnGenerateButtonClicked_())
    );
    Q_ASSERT(ConnectOK);

    ConnectOK = connect
    (
        ui->CheckBox,
        SIGNAL(stateChanged(int)),
        this,
        SLOT(OnCheckBoxStateChanged_(int))
    );
    Q_ASSERT(ConnectOK);
    Q_UNUSED(ConnectOK);
}

void Dialog::UpdateGenerateButton_()
{
    ui->GenerateButton->setEnabled(
        (!m_InputXmlFilePath.isEmpty()) &&
        (!m_InputCsvFilePath.isEmpty()));
}

void Dialog::ReadXmlFile_()
{
    QFile File(m_InputXmlFilePath);

    if (!File.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QMessageBox::critical(
            0,
            "Error!",
            "Failed to open file for reading:\n" + File.fileName());
        return;
    }

    m_XmlParser.ParseFile(File);
}

void Dialog::ReadCsvFile_()
{
    QFile File(m_InputCsvFilePath);

    if (!File.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QMessageBox::critical(
            0,
            "Error!",
            "Failed to open file for reading:\n" + File.fileName());
        return;
    }

    m_CsvParser.ParseFile(File);
}

void Dialog::FindAttributesInPanelData_()
{
    if (!CreateOutputDir_())
    {
        return;
    }

    QString StaticGSOID;
    QString RegExpPattern;
    QRegExp RegExp;
    QStringList DynamicGSOIDs;
    QStringList MatchedDynamicGSOIDs;
    QList<QPair<QString, QString> > Attributes;
    QFile OutputFile;
    QTextStream Out;

    m_OutputFileNames.clear();

    const XmlParser::PanelDataList_t& rPanelDataList = m_XmlParser.PanelDataList();

    // For each panel
    for (int k = 0; k < rPanelDataList.size(); ++k)
    {

        if (!CreateOutputFile_(k, OutputFile))
        {
            return;
        }

        Out.setDevice(&OutputFile);
        Out << m_CsvParser.TopRow()
            << c_Comma
            << c_DynamicGSOID
            << c_Comma
            << c_InitialValue
            << c_Comma
            << c_Access
            << c_Comma
            << c_Poll
            << c_Comma
            << c_AccessMatches
            << "\n";

        DynamicGSOIDs = rPanelDataList[k].uniqueKeys();

        const QVector<QString>& rStaticGSOIDs = m_CsvParser.StaticGSOIDs();
        const QVector<QString>& rStaticGSOIDsRegExp = m_CsvParser.StaticGSOIDsRegExp();
        const CsvParser::AttributeMap_t& rAttributeMap = m_CsvParser.AttributeMap();

        // Iterate through all the GSOIDs
        for (int i = 0; i < rStaticGSOIDs.size(); ++i)
        {
            StaticGSOID = rStaticGSOIDs[i];
            RegExpPattern = rStaticGSOIDsRegExp[i];
            RegExp.setPattern(RegExpPattern);
            MatchedDynamicGSOIDs = DynamicGSOIDs.filter(RegExp);
            Attributes = rAttributeMap.values(StaticGSOID);

            // All the Attribute IDs for a given GSOID
            for (int j = 0; j < Attributes.size(); ++j)
            {
                if (!MatchedDynamicGSOIDs.isEmpty())
                {
                    for(int m = 0; m < MatchedDynamicGSOIDs.size(); ++m)
                    {
                        QString AttributeID = Attributes[j].first;
                        QString Access = Attributes[j].second;
                        QString GSOID = MatchedDynamicGSOIDs[m];
                        // [Panel][GSOID][AttributeID]
                        AttributeProperties Properties =
                            rPanelDataList[k][GSOID].value(
                                AttributeID,
                                c_DefaultAttributeProperties);

                        Out << StaticGSOID
                            << c_Comma
                            << AttributeID
                            << c_Comma
                            << Access
                            << c_Comma
                            << MatchedDynamicGSOIDs[m]
                            << c_Comma
                            << Properties.m_InitialValue
                            << c_Comma
                            << Properties.m_Access
                            << c_Comma
                            << Properties.m_Poll
                            << c_Comma;

                        // Remove all non-letter characters first
                        Access.remove(c_EverythingExceptForLetters);
                        QString AccessInXml = Properties.m_Access;
                        AccessInXml.remove(c_EverythingExceptForLetters);

                        // 0 means comapre result is OK
                        if (!QString::compare(Access, AccessInXml, Qt::CaseInsensitive))
                        {
                            Out << c_True
                                << "\n";
                        }
                        else
                        {
                            Out << c_False
                                << "\n";
                        }

                        if (m_StoreFirstMatchOnly)
                        {
                            break;
                        }
                    }
                }
                else
                {
                    Out << StaticGSOID
                        << c_Comma
                        << Attributes[j].first
                        << c_Comma
                        << Attributes[j].second
                        << c_Comma
                        << c_NotFound
                        << c_Comma
                        << c_NotFound
                        << c_Comma
                        << c_NotFound
                        << c_Comma
                        << c_False
                        << c_Comma
                        << c_False
                        << "\n";
                }
            }
        }

        OutputFile.close();
    }

    QMessageBox::information(
        0,
        "Success!",
        "Generated files:\n" + m_OutputFileNames);
}

bool Dialog::CreateOutputDir_()
{
    bool Success = false;
    QDir OutputDir;
    QString OutputDirPath(m_InputXmlFilePath + "_Attributes");

    if (m_StoreFirstMatchOnly)
    {
        OutputDirPath.append("__First_Match_Only");
    }

    if (!OutputDir.mkpath(OutputDirPath))
    {
        QMessageBox::critical(
            0,
            "Error!",
            "Could not create directory:\n" + OutputDir.absolutePath());
    }
    else
    {
        m_OutputDirPath = OutputDirPath;
        Success = true;
    }

    return Success;
}

bool Dialog::CreateOutputFile_(int PanelIndex, QFile& rFile)
{
    const QStringList& rPanelIDs = m_XmlParser.PanelIDs();

    QString OutputFileName(
        m_OutputDirPath +
        '/' +
        "Panel_" +
        rPanelIDs[PanelIndex] +
        "_Attributes.csv");

    rFile.setFileName(OutputFileName);

    bool Success = false;
    if (!rFile.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QMessageBox::critical(
            0,
            "Error!",
            "Failed to open file for writing:\n" + OutputFileName);
    }
    else
    {
        m_OutputFileNames.append(OutputFileName);
        m_OutputFileNames.append("\n");
        Success = true;
    }

    return Success;
}

